Stop installing native extension build logs - #9700
Merged
Merged
Conversation
hsbt
force-pushed
the
claude/dreamy-wu-b7dbe4
branch
from
August 4, 2026 04:49
b70b6c0 to
3ee21a5
Compare
mkmf.log and gem_make.out were written into the installed extension directory, polluting the install tree and breaking bit-for-bit reproducibility checks on distros like Guix and Nix. A successful build now leaves no logs behind, and a failed build writes them to build_info (<full_name>.mkmf.log / <full_name>.gem_make.out) for inspection. https://bugs.ruby-lang.org/issues/21995 #6259 Co-Authored-By: Claude Opus 4.8 <[email protected]>
Clean up the per-gem mkmf.log and gem_make.out left in build_info by a failed extension build when the gem is uninstalled. Co-Authored-By: Claude Opus 4.8 <[email protected]>
These specs read the `make -jN` command line from `gem_make.out`, which a successful build no longer writes. The integration specs in `install_spec` now force the build to fail so the command lands in `build_info`, and the `parallel_installer` specs assert on the number of jobserver slots each gem's build acquired, which is exactly what becomes `make -jN`, instead of reading a build log. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Older system RubyGems writes the build log to the extension directory, so under RGV=system the example read the new build_info path and hit ENOENT. The `-j` suppression it verifies only exists with the jobserver support in RubyGems 4.1, so gate it like its sibling examples. Co-Authored-By: Claude Opus 4.8 <[email protected]>
hsbt
force-pushed
the
claude/dreamy-wu-b7dbe4
branch
from
August 27, 2026 07:08
53e2342 to
16c6908
Compare
Two behaviours regressed against the extension directory the logs used to live in. "clean" is the first make target and mkmf lists mkmf.log in CLEANFILES, so the log was already gone by the time a compile failure reached the handler that moves it to build_info. And nothing cleared a failed build's logs afterwards, where previously the installer wiped the extension directory on every install, so a later successful install kept reporting an old failure. Park mkmf.log next to the built extension right after extconf, the way ExtConfBuilder did before, and let build_extension decide from there whether to drop it or keep it. Drop both logs again on success, along with any gem_make.out an older RubyGems left in the extension directory. Co-Authored-By: Claude Opus 4.8 <[email protected]>
build_info entries are matched by stripping ".info", so the new <full_name>.mkmf.log and <full_name>.gem_make.out never matched an installed gem and were removed as strays, including the log the build error had just told the user to read. Let a subdirectory declare several suffixes and give build_info all three. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Logs for a git source land in bundler/gems/build_info, which `bundle clean` globs as a git checkout and reports as "Removing (build_info)" before deleting it. Exclude it the way the sibling extensions directory already is. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Preserving a log happens while a build failure is being reported, so a filesystem error there replaced the compile output the user needed with a bare Errno, and the extension builder stopped raising Gem::Ext::BuildError at all. Callers only rescue the Gem::InstallError family, so the failure escaped as an unhandled exception. Swallow errors from moving mkmf.log and from writing gem_make.out, and drop the "Results logged to" line when there is no log to point at. Co-Authored-By: Claude Opus 4.8 <[email protected]>
An extconf that exits cleanly without generating a Makefile leaves the extension unbuilt while the install still reports success. That case used to be described in gem_make.out; since the success path stopped writing one it went unrecorded entirely. Let the no-Makefile case reach Gem::Ext::Builder, which already owns where logs end up, and have it write the explanation to build_info. Co-Authored-By: Claude Opus 4.8 <[email protected]>
base_dir for a git source points at the directory holding every checkout, so logs keyed by full_name collided between revisions of the same gem and landed in bundler/gems/build_info, which nothing prunes and which `bundle clean` mistook for a stale checkout. Resolve them from extension_dir instead, which Bundler already makes unique per revision and which `bundle clean` removes along with the checkout. That also drops the clean exclusion added for the directory this no longer creates. Path sources are unaffected: Bundler installs them with extensions disabled, so they never produce a build log. Co-Authored-By: Claude Opus 4.8 <[email protected]>
The git extension spec overwrote the C source after build_git had already committed the checkout, so bundle installed the original working source and the build succeeded, leaving no log to find. Write the broken source inside the build_git block, and assert the log is the one this failure produced. ExtConfBuilder no longer swallows NoMakefileError, so on JRuby, where the fixture extconf returns before creating a Makefile, calling the class method directly now raises instead of returning. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Where a build log goes is decided by the RubyGems running the install, and under RGV=system that is an older one which writes a bare gem_make.out into the extension directory. Gate the example the way the other specs that assert on log locations already are. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Building a C extension writes
mkmf.logandgem_make.outinto the installed extension directory underextensions/. Those logs are noisy and non-reproducible, so they pollute the install tree and break bit-for-bit reproducibility checks on distributions like Guix and Nix that verify build output.A successful build now leaves no logs anywhere in the installation tree, neither in the extension directory nor in the unpacked gem source
extdirectory. A failed build still keeps the logs for inspection, but writes them to thebuild_infodirectory as<full_name>.mkmf.logand<full_name>.gem_make.out, next to the existing<full_name>.info. The build error message points at those new paths, and uninstalling the gem removes both files.This changes a long-standing behavior.
gem_make.outwas previously always present in the extension directory after a successful build. Anything that relied on reading that file on success will no longer find it, and on failure it now lives inbuild_inforather than the extension directory.A failed build now reports the log location like this.
Refs:
https://bugs.ruby-lang.org/issues/21995
#6259